-- -------------------------------------------------------------------------
-- Config/features.lua (Feature State Configuration System)
-- Saves and loads feature on/off states across sessions
-- -------------------------------------------------------------------------

local features_config = {}

-- Uses local json library (pure Lua)
local json_lib = require("ClickUl.Lib.json")
local debugger = nil

-- [Lua 5.4] <const> for function references
local type <const> = type
local pairs <const> = pairs
local pcall <const> = pcall
local os_clock <const> = os.clock

-- [Internal state]
local data = {}
local dirty = false
local initialized = false
local auto_save_enabled = true  -- Global toggle for auto-save feature

-- [Lua 5.4] <const> for throttle configuration
local last_save_time = 0
local last_set_module_time = 0
local SAVE_THROTTLE_INTERVAL <const> = 5.0
local SET_MODULE_THROTTLE <const> = 2.0     -- Increased from 0.5 to 2.0 to reduce save frequency


-- [Path Helpers]


local function get_base_dir()
    local base = ".\\"
    if paths and paths.script then
        base = paths.script
    end

    local last_char = string.sub(base, -1)
    if last_char ~= "\\" and last_char ~= "/" then
        base = base .. "\\"
    end

    return base .. "ClickUl\\"
end

local function get_config_path()
    return get_base_dir() .. "Config\\features.json"
end

-- Safe file close helper (Lexis API compatibility)
local function safe_close(f)
    if not f then return end
    if type(f.close) == "function" then
        pcall(f.close, f)
    elseif type(f.Close) == "function" then
        pcall(f.Close, f)
    end
end


-- [JSON Helpers]

local function encode_json(tbl)
    local ok, result = pcall(json_lib.encode_pretty, tbl, "  ")
    if ok and result and #result > 0 then return result end
    return nil, result
end

local function decode_json(str)
    local ok, result = pcall(json_lib.decode, str)
    if ok and result then return result end
    return nil, result
end


-- [Core API]


--- Initialize the features config system
-- @param _ unused (previously json_lib, now using native json)
-- @param debug_lib table Optional debugger library
function features_config.init(_, debug_lib)
    debugger = debug_lib

    if debugger then
        debugger.info("[FeaturesConfig] Initializing")
    end

    -- Load existing config
    features_config.load()
    initialized = true
end

--- Get a feature value
-- @param module_name string The module name (e.g., "Self", "Vehicle")
-- @param key string The feature key (e.g., "god_mode", "fast_run")
-- @param default any Default value if not found
-- @return any The stored value or default
function features_config.get(module_name, key, default)
    if not data[module_name] then
        return default
    end
    local val = data[module_name][key]
    if val == nil then
        return default
    end
    return val
end

--- Set a feature value
-- @param module_name string The module name
-- @param key string The feature key
-- @param value any The value to store
function features_config.set(module_name, key, value)
    if not data[module_name] then
        data[module_name] = {}
    end

    -- Only mark dirty if value actually changed
    if data[module_name][key] ~= value then
        data[module_name][key] = value
        dirty = true
    end
end

--- Get all values for a module
-- @param module_name string The module name
-- @return table All stored values for the module
function features_config.get_module(module_name)
    return data[module_name] or {}
end

--- [PERF OPT] Set multiple values for a module at once (throttled)
--- Skips update if called too frequently to avoid excessive saves
-- @param module_name string The module name
-- @param values table Key-value pairs to set
-- @param force boolean|nil If true, bypass throttle
function features_config.set_module(module_name, values, force)
    if type(values) ~= "table" then return end

    local now = os_clock()
    if not force and (now - last_set_module_time) < SET_MODULE_THROTTLE then
        return
    end
    last_set_module_time = now

    if not data[module_name] then
        data[module_name] = {}
    end

    for k, v in pairs(values) do
        if data[module_name][k] ~= v then
            data[module_name][k] = v
            dirty = true
        end
    end
end

--- Check if config has unsaved changes
-- @return boolean True if there are unsaved changes
function features_config.is_dirty()
    return dirty
end

--- Save config to file
-- @return boolean True on success
function features_config.save()
    if not json_lib then
        if debugger then debugger.warn("[FeaturesConfig] Cannot save: JSON lib missing") end
        return false
    end

    local config_path = get_config_path()
    local dir_path = get_base_dir() .. "Config\\"

    -- Create directory if needed
    if dirs and dirs.create then
        pcall(dirs.create, dir_path)
    end

    local content, enc_err = encode_json(data)
    if not content then
        if debugger then debugger.error("[FeaturesConfig] JSON encode failed", {error = enc_err}) end
        return false
    end

    -- Try native file API only
    if file and file.open then
        local f = nil
        local ok, result = pcall(function()
            f = file.open(config_path, {create_if_not_exists = true, append = false})
            if f and (f.valid == nil or f.valid) then
                f.text = content
                return true
            end
            return false
        end)
        if f then safe_close(f) end

        if ok and result then
            dirty = false
            if debugger then debugger.info("[FeaturesConfig] Saved successfully") end
            return true
        end

        if debugger then debugger.error("[FeaturesConfig] Save failed", {error = result}) end
        return false
    end

    if debugger then debugger.warn("[FeaturesConfig] Cannot save: file API missing") end
    return false
end

--- Load config from file
-- @return boolean True on success
function features_config.load()
    if not json_lib then
        if debugger then debugger.warn("[FeaturesConfig] Cannot load: JSON lib missing") end
        return false
    end

    local config_path = get_config_path()
    local content = nil

    -- Try native file API only
    if file and file.open and file.exists then
        local ok, result = pcall(function()
            if file.exists(config_path) then
                local f = file.open(config_path, {create_if_not_exists = false, append = false})
                if f and f.valid then
                    local text = f.text
                    safe_close(f)
                    return text
                end
            end
            return nil
        end)

        if ok then
            content = result
        end
    else
        if debugger then debugger.warn("[FeaturesConfig] Cannot load: file API missing") end
    end

    -- No config file exists yet - that's fine
    if not content or string.len(content) < 2 then
        data = {}
        if debugger then debugger.debug("[FeaturesConfig] No config file found, using defaults") end
        return true
    end

    local decoded, dec_err = decode_json(content)
    if decoded and type(decoded) == "table" then
        data = decoded
        dirty = false
        -- Restore auto_save_enabled setting
        if data._settings and data._settings.auto_save_enabled ~= nil then
            auto_save_enabled = data._settings.auto_save_enabled
        end
        if debugger then debugger.info("[FeaturesConfig] Loaded successfully") end
        return true
    else
        if debugger then debugger.warn("[FeaturesConfig] JSON decode failed", {error = dec_err}) end
        data = {}
        return false
    end
end

--- Reset all config data
function features_config.reset()
    data = {}
    dirty = true
end

--- Reset a specific module's config
-- @param module_name string The module to reset
function features_config.reset_module(module_name)
    if data[module_name] then
        data[module_name] = nil
        dirty = true
    end
end

--- [PERF OPT] Auto-save with throttling
--- Only saves if dirty AND enough time has passed since last save
--- This prevents 60+ saves/sec when user drags sliders
function features_config.auto_save()
    if not dirty or not initialized or not auto_save_enabled then
        return false
    end

    local now = os_clock()
    if (now - last_save_time) < SAVE_THROTTLE_INTERVAL then
        return false  -- Throttled, skip this save
    end

    last_save_time = now
    return features_config.save()
end

--- Check if auto-save is enabled
-- @return boolean True if auto-save is enabled
function features_config.is_enabled()
    return auto_save_enabled
end

--- Enable or disable auto-save
-- @param enabled boolean Enable or disable
function features_config.set_enabled(enabled)
    auto_save_enabled = enabled
    -- Store in settings section
    if not data._settings then
        data._settings = {}
    end
    data._settings.auto_save_enabled = enabled
    dirty = true
end

--- Force save now (regardless of enabled state)
function features_config.force_save()
    if initialized then
        return features_config.save()
    end
    return false
end


-- [Language Config API] - Separated from theme config

--- Get current language setting
-- @return string The current language code (e.g., "zh_cn", "en")
function features_config.get_language()
    if not data._settings then
        data._settings = {}
    end
    return data._settings.language or nil
end

--- Set language setting
-- @param lang_code string The language code to save
-- @return boolean True on success
function features_config.set_language(lang_code)
    if type(lang_code) ~= "string" then
        return false
    end

    if not data._settings then
        data._settings = {}
    end

    -- Only update if changed
    if data._settings.language ~= lang_code then
        data._settings.language = lang_code
        dirty = true
        if debugger then
            debugger.debug("[FeaturesConfig] Language changed to: " .. lang_code)
        end
    end

    return true
end

--- Check if language config exists
-- @return boolean True if language has been set
function features_config.has_language()
    return data._settings and data._settings.language and data._settings.language ~= ""
end


-- [Debug Config API] - Separated from theme config

--- Get debug configuration
-- @return table The debug configuration table
function features_config.get_debug_config()
    if not data._settings then
        data._settings = {}
    end
    return data._settings.debug or {}
end

--- Set debug configuration
-- @param debug_cfg table The debug configuration to save
-- @return boolean True on success
function features_config.set_debug_config(debug_cfg)
    if type(debug_cfg) ~= "table" then
        return false
    end

    if not data._settings then
        data._settings = {}
    end

    -- Only update if changed
    local changed = false
    local current = data._settings.debug or {}

    for k, v in pairs(debug_cfg) do
        if current[k] ~= v then
            changed = true
            break
        end
    end

    if changed then
        data._settings.debug = debug_cfg
        dirty = true
        if debugger then
            debugger.debug("[FeaturesConfig] Debug config updated")
        end
    end

    return true
end

--- Check if debug config exists
-- @return boolean True if debug config has been set
function features_config.has_debug_config()
    return data._settings and data._settings.debug and next(data._settings.debug) ~= nil
end

return features_config
